/* Copyright 2021 Kerim Yalcin
**
**   Licensed under the Apache License, Version 2.0 (the "License");
**   you may not use this file except in compliance with the License.
**   You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
**   Unless required by applicable law or agreed to in writing, software
**   distributed under the License is distributed on an "AS IS" BASIS,
**   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
**   See the License for the specific language governing permissions and
**   limitations under the License.
*/

#include "Directory.hpp"

#include <iomanip>

/***** private *****/

void Agx::Directory::print_inSystemTimeFormat(const SYSTEMTIME& pTime) const {
    std::cout << pTime.wYear
        << "-" << std::setfill('0') << std::setw(2) << pTime.wMonth
        << "-" << std::setfill('0') << std::setw(2) << pTime.wDay
        << " " << std::setfill('0') << std::setw(2) << pTime.wHour
        << ":" << std::setfill('0') << std::setw(2) << pTime.wMinute
        << ":" << std::setfill('0') << std::setw(2) << pTime.wSecond
        << std::setfill(' ') << std::setw(0);
}

/***** public *****/

Agx::Directory::Directory() :
    mError {Agx::Error("Directory")},
    mSearchHandle {nullptr},
    mFindData {0},
    mHas_found_noFile {false},
    mHas_found_noMoreFiles {false},

    mIs_DefaultSecurityDescriptor_enabled {true},
    mIs_TemplateDirectory_enabled {false}
{}

void Agx::Directory::set_Path(const std::string& pAbsolutePath) {
    mPath = pAbsolutePath;
}

const std::string& Agx::Directory::get_Path() {
    return mPath;
}

const std::string& Agx::Directory::get_Path_Absolute_determined() {
    DWORD size = GetFullPathNameA(
        mPath.c_str(),
        0,
        nullptr,
        nullptr
    );

    if (size == 0) {
        mError.print_fromWin32("get_Path_Absolute_determined");
        mPath_Absolute_determined = std::string();
    }
    else {
        CHAR path[size];
        LPSTR buf = path;

        GetFullPathNameA(
            mPath.c_str(),
            size,
            buf,
            nullptr
        );

        mPath_Absolute_determined = std::string(buf, size);
    }
    return mPath_Absolute_determined;
}

const std::string& Agx::Directory::get_Path_Absolute_verified() {
    WIN32_FIND_DATAA tmp = {0};
    HANDLE search = nullptr;
    bool hasFoundVerified = false;
    mPath_Absolute_verified = std::string();

    search = FindFirstFileA(
        mPath.c_str(),
        &tmp
    );
    if (search == INVALID_HANDLE_VALUE) {
        mError.print_fromWin32("get_Path_Absolute_verified");
    }
    else {
        hasFoundVerified = true;
        FindClose(search);
    }

    if (hasFoundVerified) {
        DWORD size = GetFullPathNameA(
            mPath.c_str(),
            0,
            nullptr,
            nullptr
        );

        if (size == 0) {
            mError.print_fromWin32("get_Path_Absolute_verified");
        }
        else {
            CHAR path[size];
            LPSTR buf = path;

            GetFullPathNameA(
                mPath.c_str(),
                size,
                buf,
                nullptr
            );

            mPath_Absolute_verified = std::string(buf, size);
        }
    }

    return mPath_Absolute_verified;
}

void Agx::Directory::set_Path_ofCurrentProcess(const std::string& pPath_ofCurrentProcess) {
    mPath_ofCurrentProcess = pPath_ofCurrentProcess;
    if (
        !SetCurrentDirectoryA(
            mPath_ofCurrentProcess.c_str()
        )
    ) {
        mError.print_fromWin32("set_Directory_ofCurrentProcess");
    }
}

const std::string& Agx::Directory::get_Path_ofCurrentProcess() {
    DWORD size = GetCurrentDirectoryA(0, nullptr);

    if (size != 0) {
        CHAR path[size];
        LPSTR buf = &path[0];

        if(GetCurrentDirectoryA(size, buf)) {
            mPath_ofCurrentProcess = std::string(buf, size);
        }
        else {
            mError.print_fromWin32("get_PathName_ofCurrentProcess");
            mPath_ofCurrentProcess = std::string();
        }
    }

    return mPath_ofCurrentProcess;
}

void Agx::Directory::enable_DefaultSecurityDescriptor() {
    mIs_DefaultSecurityDescriptor_enabled = true;
}

void Agx::Directory::disable_DefaultSecurityDescriptor() {
    mIs_DefaultSecurityDescriptor_enabled = false;
}

void Agx::Directory::create_Directory() {
    if(
        !CreateDirectoryA(
            mPath.c_str(),
            mIs_DefaultSecurityDescriptor_enabled ? nullptr : mSecurityAttributes
        )
    ) {
        mError.print_fromWin32("create_Directory");
    }
}

void Agx::Directory::remove_Directory() {
    if(
        !RemoveDirectoryA(
            mPath.c_str()
        )
    ) {
        mError.print_fromWin32("remove_Directory");
    }
}

void Agx::Directory::find() {
    mFindData = {0};
    mSearchHandle = FindFirstFileA(
        mPath.c_str(),
        &mFindData
    );
    if (mSearchHandle == INVALID_HANDLE_VALUE) {
        mError.print_fromWin32("find");
        mHas_found_noFile = true;
    }
    else {
        mHas_found_noFile = false;
    }
}

void Agx::Directory::find_next() {
    if (mSearchHandle) {
        mFindData = {0};
        if(
            !FindNextFileA(
                mSearchHandle,
                &mFindData
            )
        ) {
            mError.print_fromWin32("find_next");
            mHas_found_noMoreFiles = true;
            FindClose(mSearchHandle);
            mSearchHandle = nullptr;
        }
        else {
            mHas_found_noMoreFiles = false;
        }
    }
}

void Agx::Directory::find_print() {
    find_print_FileAttribute();
    find_print_CreationTime();
    find_print_LastAccessTime();
    find_print_LastWriteTime();
    find_print_FileSize();
    find_print_FileName();
    std::cout << std::endl;
}

void Agx::Directory::find_print_FileAttribute() {
    std::string fileAttribute;

    if (mFindData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
        fileAttribute+="FILE_ATTRIBUTE_ARCHIVE";
        fileAttribute+=" | ";
    }
    if (mFindData.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) {
        fileAttribute+="FILE_ATTRIBUTE_COMPRESSED";
        fileAttribute+=" | ";
    }
    if (mFindData.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) {
        fileAttribute+="FILE_ATTRIBUTE_DEVICE";
        fileAttribute+=" | ";
    }
    if (mFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
        fileAttribute+="FILE_ATTRIBUTE_DIRECTORY";
        fileAttribute+=" | ";
    }
    if (mFindData.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) {
        fileAttribute+="FILE_ATTRIBUTE_ENCRYPTED";
        fileAttribute+=" | ";
    }
    if (mFindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
        fileAttribute+="FILE_ATTRIBUTE_HIDDEN";
        fileAttribute+=" | ";
    }
    if (mFindData.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) {
        fileAttribute+="FILE_ATTRIBUTE_NORMAL";
        fileAttribute+=" | ";
    }
    if (mFindData.dwFileAttributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) {
        fileAttribute+="FILE_ATTRIBUTE_NOT_CONTENT_INDEXED";
        fileAttribute+=" | ";
    }
    if (mFindData.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) {
        fileAttribute+="FILE_ATTRIBUTE_OFFLINE";
        fileAttribute+=" | ";
    }
    if (mFindData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
        fileAttribute+="FILE_ATTRIBUTE_READONLY";
        fileAttribute+=" | ";
    }
    if (mFindData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
        fileAttribute+="FILE_ATTRIBUTE_REPARSE_POINT";
        fileAttribute+=" | ";
    }
    if (mFindData.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) {
        fileAttribute+="FILE_ATTRIBUTE_SPARSE_FILE";
        fileAttribute+=" | ";
    }
    if (mFindData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
        fileAttribute+="FILE_ATTRIBUTE_SYSTEM";
        fileAttribute+=" | ";
    }
    if (mFindData.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
        fileAttribute+="FILE_ATTRIBUTE_TEMPORARY";
        fileAttribute+=" | ";
    }
    if (mFindData.dwFileAttributes & FILE_ATTRIBUTE_VIRTUAL) {
        fileAttribute+="FILE_ATTRIBUTE_VIRTUAL";
        fileAttribute+=" | ";
    }

    if(fileAttribute.empty()) {
        std::cout << "FileAttributes: " << "<empty>" << std::endl;
    }
    else {
        fileAttribute.erase(fileAttribute.end()-3, fileAttribute.end()); 
        std::cout << "FileAttributes: " << fileAttribute << std::endl;
    }
}

void Agx::Directory::find_print_CreationTime() {
    SYSTEMTIME time = {0};

    if(
        FileTimeToSystemTime(
            &mFindData.ftCreationTime,
            &time
        )
    ) {
        std::cout << "CreationTime: ";
        print_inSystemTimeFormat(time);
        std::cout << std::endl;
    }
    else {
        std::cout << "CreationTime: <empty>" << std::endl;
    }
}

void Agx::Directory::find_print_LastAccessTime() {
    SYSTEMTIME time = {0};

    if(
        FileTimeToSystemTime(
            &mFindData.ftLastAccessTime,
            &time
        )
    ) {
        std::cout << "LastAccessTime: ";
        print_inSystemTimeFormat(time);
        std::cout << std::endl;
    }
    else {
        std::cout << "LastAccessTime: <empty>" << std::endl;
    }
}

void Agx::Directory::find_print_LastWriteTime() {
    SYSTEMTIME time = {0};

    if(
        FileTimeToSystemTime(
            &mFindData.ftLastWriteTime,
            &time
        )
    ) {
        std::cout << "LastWriteTime: ";
        print_inSystemTimeFormat(time);
        std::cout << std::endl;
    }
    else {
        std::cout << "LastWriteTime: <empty>" << std::endl;
    }
}

void Agx::Directory::find_print_FileSize() {
    std::cout << "FileSize: "
        << (mFindData.nFileSizeHigh*(MAXDWORD-1))+mFindData.nFileSizeLow
        << " B" << std::endl;
}

void Agx::Directory::find_print_FileName() {
    if (*mFindData.cFileName != 0x00) {
        std::cout << "FileName: " << mFindData.cFileName << std::endl;
    }
    else {
        std::cout << "FileName: <empty>" << std::endl;
    }
}

const bool& Agx::Directory::has_found_noFile() const {
    return mHas_found_noFile;
}

const bool& Agx::Directory::has_found_noMoreFiles() const {
    return mHas_found_noMoreFiles;
}

void Agx::Directory::set_Path_ofTemplateDirectory(const std::string& pPathName) {
    mPath_ofTemplateDirectory = pPathName;
}

const std::string& Agx::Directory::get_Path_ofTemplateDirectory() {
    return mPath_ofTemplateDirectory;
}

void Agx::Directory::create_Directory_fromTemplate() {
    if (mIs_TemplateDirectory_enabled) {
        if (
            !CreateDirectoryExA(
                mPath_ofTemplateDirectory.c_str(),
                mPath.c_str(),
                mIs_DefaultSecurityDescriptor_enabled ? nullptr : mSecurityAttributes
            )
        ) {
            mError.print_fromWin32("create_Directory_fromTemplate");
        } 
    }
}

void Agx::Directory::enable_SecurityAttributes_InheritHandle() {
    mSecurityAttributes->bInheritHandle = true;
}

void Agx::Directory::disable_SecurityAttributes_InheritHandle() {
    mSecurityAttributes->bInheritHandle = false;
}

void Agx::Directory::init_SecurityDescriptor() {
    if (
        !InitializeSecurityDescriptor(
            mSecurityDescriptor,
            SECURITY_DESCRIPTOR_REVISION
        )
    ) {
        mError.print_fromWin32("init_SecurityDescriptor");
    }
}

bool Agx::Directory::is_valid_SecurityDescriptor() {
    return IsValidSecurityDescriptor(mSecurityDescriptor) ? true : false;
}